package org.uninova.mobis.servlets;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import net.sf.json.JSONObject;

import org.uninova.mobis.api.interfaces.open.routers.RoutinoInterface;
import org.uninova.mobis.api.interfaces.swedish.ResRobotJourneyPlannerInterface;
import org.uninova.mobis.constants.StringConstants;
import org.uninova.mobis.db.GTFSDBConnector;
import org.uninova.mobis.pojos.Coordinate;
import org.uninova.mobis.pojos.ResRobotSegment;
import org.uninova.mobis.utils.DBUtilsImpl;

/**
 * Servlet implementation class GetDirectionsResRobotServlet
 * @author PAF
 */
@WebServlet("/GetDirectionsResRobotServlet")
public class GetDirectionsResRobotServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public GetDirectionsResRobotServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		processRequest(request, response) ;
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		processRequest(request, response) ;
	}
	
	/**
	 * This servlet handles the multi-modal route planning for Sweden. It uses the ResRobot router and the Routino router 
	 * to calculate multi-modal routes for all kinds of public transportation
	 * 
	 * @param segNums		The IDs of the segments for the route, separated by a comma (,)
	 * @param segmentsCount	The total number of segments on the route
	 * 
	 * @return				The route given as a JSON string, separated by segments for a particular means of transport (foot, bus, metro, etc…)
	 */
	protected void processRequest(HttpServletRequest request, HttpServletResponse response) {
		String segNums = request.getParameter("segNums") ;
		int segmentsCount = Integer.parseInt(request.getParameter("segments")) ;
		
		ResRobotJourneyPlannerInterface resRobotInterface = new ResRobotJourneyPlannerInterface() ;
		GTFSDBConnector connector = new GTFSDBConnector() ;
		RoutinoInterface routinoInt = new RoutinoInterface() ;
		DBUtilsImpl dbUtils = new DBUtilsImpl() ;
		Connection con = dbUtils.startConnection() ;
		HttpSession session = request.getSession() ;
		String result = (String) session.getAttribute("resRobotResult") ;
		
		ArrayList<Coordinate> coords ;
		ArrayList<Double> lats, lngs ;
		String routinoResult, instructions ;
		
		ArrayList<Integer> segmentNums = new ArrayList<Integer>() ;
		
		if (segmentsCount > 1) {
			for (int i = 0; i < segmentsCount - 1; i++) {
				segmentNums.add(Integer.parseInt(segNums.substring(0, segNums.indexOf(",")))) ;
				segNums = segNums.substring(segNums.indexOf(",") + 1) ;
			}
		}
		segmentNums.add(Integer.parseInt(segNums)) ;
		
		/* While Routino's foot routing does not work, we will use this profile with transportation type = 'bicycle' */
		HashMap<String, Integer> highwayPrefs = new HashMap<String, Integer>() ;
		HashMap<String, Integer> speedPrefs = new HashMap<String, Integer>() ;
		HashMap<String, Integer> propertyPrefs = new HashMap<String, Integer>() ;
		
		// IF ROUTINO CANNOT FIND ROUTE BY FOOT, WE WILL HAVE TO MAKE ANOTHER SEARCH FOR TRANSPORT TYPE = 'BICYCLE', AND USE THE BELOW FOOT PREFERENCES
		speedPrefs.put("motorway", 0) ;
		speedPrefs.put("trunk", 4) ;
		speedPrefs.put("primary", 4) ;
		speedPrefs.put("secondary", 4) ;
		speedPrefs.put("tertiary", 4) ;
		speedPrefs.put("unclassified", 4) ;
		speedPrefs.put("residential", 4) ;
		speedPrefs.put("service", 4) ;
		speedPrefs.put("track", 4) ;
		speedPrefs.put("cycleway", 4) ;
		speedPrefs.put("path", 4) ;
		speedPrefs.put("steps", 4) ;
		speedPrefs.put("ferry", 0) ;
		
		highwayPrefs.put("motorway", 0) ;
		highwayPrefs.put("trunk", 40) ;
		highwayPrefs.put("primary", 50) ;
		highwayPrefs.put("secondary", 60) ;
		highwayPrefs.put("tertiary", 70) ;
		highwayPrefs.put("unclassified", 80) ;
		highwayPrefs.put("residential", 90) ;
		highwayPrefs.put("service", 95) ;
		highwayPrefs.put("track", 95) ;
		highwayPrefs.put("cycleway", 95) ;
		highwayPrefs.put("path", 100) ;
		highwayPrefs.put("steps", 80) ;
		highwayPrefs.put("ferry", 0) ;
		
		propertyPrefs.put("paved", 50) ;
		propertyPrefs.put("multilane", 25) ;
		propertyPrefs.put("bridge", 50) ;
		propertyPrefs.put("tunnel", 50) ;
		propertyPrefs.put("footroute", 55) ;
		propertyPrefs.put("bicycleroute", 55) ;
	
		String[] results = new String[segmentsCount] ;
		// HANDLES THE RESULT OF SERVLET 'GetResRobotRoutes', KEPT AS SESSION VARIABLE. 
		// IF THE ROUTE HAS AT LEAST ONE WAYPOINT, THEN THE RESULT IS DIVIDED INTO SEVERAL JSON OBJECTS, SEPARATED BY THE STRING '!!'
		for (int i = 0; i < segmentsCount; i++) {
			if (result.contains("!!")) {
				results[i] = result.substring(0, result.indexOf("!!")) ;
				result = result.substring(result.indexOf("!!") + 2) ;
			}
			else {
				results[i] = result ;
			}
		}
		
		String jsonResponse = "{\"route\":[" ;
		JSONObject resObject ;
		ArrayList<ResRobotSegment> resRobotSegments ;
		int segNum ;
		File f ;
		
		for (int i = 0; i < segmentNums.size(); i++) {
			segNum = segmentNums.get(i) ;
			resObject = JSONObject.fromObject(results[i]) ;
			resRobotSegments = resRobotInterface.parseSearchResponse(resObject, segNum) ;
			
			for (ResRobotSegment seg : resRobotSegments) {
				// IF THE MEANS OF TRANSPORTATION (MOT) IS WALKING
				if (seg.getMotType().equals(StringConstants.WALKING_DISPLAY_TYPE) || seg.getMotType().equals(StringConstants.WALK_LONG_DISTANCE_DISPLAY_TYPE)) {
					//TODO: get foot route from Routino
					lats = new ArrayList<Double>() ;
					lngs = new ArrayList<Double>() ;
					lats.add(seg.getDepartureStationCoord().lat) ;
					lats.add(seg.getArrivalStationCoord().lat) ;
					lngs.add(seg.getDepartureStationCoord().lng) ;
					lngs.add(seg.getArrivalStationCoord().lng) ;
					
					//CALCULATE ROUTINO ROUTE FOR FOOT
					
					routinoResult = routinoInt.initRoutinoRouter("/home/paf/routino_data", "sv", "", "", false, false, false, "", "", "foot", false, lats, lngs, null, null, null, false, false, 0.0, 0.0, 0.0, 0.0) ;
					if (routinoResult != null && !routinoResult.equals("")) {
						routinoResult = routinoResult.replace("\"", "'") ;
					}
					else {
						routinoResult = routinoInt.initRoutinoRouter("/home/paf/routino_data", "sv", "", "", false, false, false, "", "", "bicycle", false, lats, lngs, highwayPrefs, speedPrefs, propertyPrefs, false, false, 0.0, 0.0, 0.0, 0.0) ;
						routinoResult = routinoResult.replace("\"", "'") ;
					}
					
					// GET THE ROUTE'S HTML INSTRUCTIONS CREATED BY ROUTINO
					instructions = routinoInt.readRouttinoInstructions(false) ;
					instructions = instructions.replace("\"", "'") ;
					jsonResponse = resRobotInterface.generateResRobotSegmentResponse(seg, instructions, null, routinoResult, jsonResponse, ",") ;
				}
				// IF THE MEANS OF TRANSPORTATION (MOT) IS TAXI OR BUS
				else if (seg.getMotType().equals(StringConstants.AIRPORT_BUS_DISPLAY_TYPE) || seg.getMotType().equals(StringConstants.AIRPORT_TAXI_DISPLAY_TYPE) || seg.getMotType().equals(StringConstants.BUS_DISPLAY_TYPE) || seg.getMotType().equals(StringConstants.TAXI_DISPLAY_TYPE)) {
					
					// GET THE COORDINATES FOR THE STOPS BETWEEN DEPARTURE AND ARRIVAL
					coords = connector.getSegmentCoordinates(dbUtils, con, seg) ;
					lats = new ArrayList<Double>() ;
					lngs = new ArrayList<Double>() ;
					
					if (coords.size() < 2) {
						lats.add(seg.getDepartureStationCoord().lat) ;
						lats.add(seg.getArrivalStationCoord().lat) ;
						lngs.add(seg.getDepartureStationCoord().lng) ;
						lngs.add(seg.getArrivalStationCoord().lng) ;
					}
					else {
						for (Coordinate coord : coords) {
							lats.add(coord.lat) ;
							lngs.add(coord.lng) ;
						}
					}
					
					//CALCULATE ROUTINO ROUTE WITH THE STOP COORDINATES AS WAYPOINTS (ASSURING THAT THE ROUTE PASSES THROUGH ALL THE STOPS)
					
					routinoResult = routinoInt.initRoutinoRouter("/home/paf/routino_data", "sv", "", "", false, false, false, "", "", "psv", true, lats, lngs, null, null, null, true, true, 0.0, 0.0, 0.0, 0.0) ;
					f = new File("shortest.html") ;
					f.delete() ;
			
					if (routinoResult != null) {
						routinoResult = routinoResult.replace("\"", "'") ;
						jsonResponse = resRobotInterface.generateResRobotSegmentResponse(seg, "", null, routinoResult, jsonResponse, ",") ;
					}
					// IF THE ROUTINO RESPONSE IS EMPTY, CALCULATE NEW ROUTINO ROUTE WITH JUST THE DEPARTURE AND ARRIVAL COORDINATES
					else {
						lats = new ArrayList<Double>() ;
						lngs = new ArrayList<Double>() ;
						lats.add(seg.getDepartureStationCoord().lat) ;
						lats.add(seg.getArrivalStationCoord().lat) ;
						lngs.add(seg.getDepartureStationCoord().lng) ;
						lngs.add(seg.getArrivalStationCoord().lng) ;
						routinoResult = routinoInt.initRoutinoRouter("/home/paf/routino_data", "sv", "", "", false, false, false, "", "", "motorcar", true, lats, lngs, null, null, null, true, true, 0.0, 0.0, 0.0, 0.0) ;
						if (routinoResult != null) {
							routinoResult = routinoResult.replace("\"", "'") ;
							jsonResponse = resRobotInterface.generateResRobotSegmentResponse(seg, "", null, routinoResult, jsonResponse, ",") ;
						}
						else
							jsonResponse = resRobotInterface.generateResRobotSegmentResponse(seg, "", coords, null, jsonResponse, ",") ;
					}
				}
				// IF THE MEANS OF TRANSPORTATION (MOT) IS TRAIN, METRO OR TRAM
				else {
					coords = connector.getSegmentCoordinates(dbUtils, con, seg) ;
					if (coords.size() < 2) {
						coords = new ArrayList<Coordinate>() ;
						coords.add(seg.getDepartureStationCoord()) ;
						coords.add(seg.getArrivalStationCoord()) ;
					}
					jsonResponse = resRobotInterface.generateResRobotSegmentResponse(seg, "", coords, "", jsonResponse, ",") ;
					
				}
			}
		}
		jsonResponse = jsonResponse.substring(0, jsonResponse.lastIndexOf(",")) ;
		jsonResponse += "]}" ;
		session.removeAttribute("resRobotResult") ;
		try {
			PrintWriter out = response.getWriter() ;
			out.println(jsonResponse) ;
			out.close() ;
			con.close() ;
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
